/*
 * RuntimeVCDReader.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */


package engine;

import middle.*;
import java.util.*;
import java.io.*;

/**
 * A smarter VCDReader (@see middle.VCDReader)
 */
public class RuntimeVCDReader extends DummyVCDReader{

	TimeUnitChangeListener externalListener;
	static final String swapFileName = "swapFile.swp";

	//here we could use a hashtable, but, unfortunetely
	//it does not allow null values
	//and, hashmap, wich allows it, does not garantees
	//the order, which is essential here
	//for data to be correctly written into the file
	Vector dataHolders, varHistories; 

	RandomAccessFile swapFile;
	long varNumber;

	RuntimeVCDReader(TimeUnitChangeListener externalListener){
		this.externalListener = externalListener;
		dataHolders = new Vector();
		varHistories = new Vector();
		varNumber = 0;
		try{
			new File(swapFileName).delete();
		}catch(SecurityException ex){
			xConsole.debug("Security exception while trying to delete the swap file");
		}
		try{
			swapFile = new RandomAccessFile(swapFileName,"rw");
		}catch(FileNotFoundException ex){}
	}

	public void finalize(){
		try{
			swapFile.close();
			new File(swapFileName).delete();
		}catch(IOException ex){}
		catch(SecurityException ex){
			xConsole.debug("Security exception while trying to delete the swap file");
		}
	}

	//VCDReader implementation

	/**
	 * Returns a new VariabeHistory for the variable named FQN
	 */
	public VariableHistory getVarHistory(String fqName){
		int memSpecIndex = -1, memIndex;
		Object o;
		RuntimeVarHistory h;

		fqName = "@top" + fqName;

		if(!Time.keepWorking)return null; //no simulation, so don't try anything
		xConsole.debug("adding wave for:" + fqName);
		if(fqName.lastIndexOf(']') == fqName.length()-1) {
			memSpecIndex = fqName.lastIndexOf('[');
		}

		try{
			FQN fqn = null;
			if(memSpecIndex == -1){
				//not a memory 
				memIndex = 0;
				o = main.topModule.resolveName(fqn = FQN.parseString(fqName));
			}else{
				o = main.topModule.resolveName(FQN.parseString(fqName.substring(0,
						memSpecIndex-1)));
				memIndex = Integer.parseInt(fqName.substring(memSpecIndex+1,
						fqName.length()));

			}
		}catch(Exception ex){
			xConsole.dumpStack(ex);
			xConsole.debug("RuntimeVCDReader.getVarHistory(" + fqName + "): " + ex);
			return null;
		}

		xConsole.debug("object is: " + o);

		if(o != null){
			xConsole.debug("ok, now guessing type for it");
		}else{
			xConsole.debug("getVarHistory failed on: " + fqName);
			return (RuntimeVarHistory) null;
		}

		//now try to guess the type of the variable:

		try{
			//register ?
			Register r = (Register) o;
			BitVector b = null;
			b = r.getData(memIndex);
			h = new RuntimeVarHistory(b.n == 1 ? VariableHistory.SCALAR : VariableHistory.VECTOR, (int)getSimulationLength());
		}catch(ClassCastException ex){
			try{
				//Wire ?
				BitVector b = (BitVector) o;
				h = new RuntimeVarHistory(b.n == 1 ? VariableHistory.SCALAR : VariableHistory.VECTOR, (int)getSimulationLength());
			}catch(ClassCastException exWire){
				try{
					//Real ?
					Real r = (Real) o;
					h = new RuntimeVarHistory(VariableHistory.VECTOR, (int)getSimulationLength());
				}catch(ClassCastException exReal){
					xConsole.debug("getVarHistory failed on: " + fqName);
					return (RuntimeVarHistory) null;
				}
			} // catch for wire
		} // catch for register

		try{
			swapFile.seek(0);
		}catch(IOException ex){
			throw new Error(ex.getMessage());
		}
		String s;
		int pos = 0, myVarPos = 0;

		do{ //search the position of the variable inside the file
			try{
				s = swapFile.readLine();
				xConsole.debug("string: " + s);

			}catch(IOException ex){
				throw new Error(ex.getMessage());
			}
			myVarPos++;
		}while(!s.equals(fqName) && myVarPos < varNumber);
		myVarPos--;

		{//skip to the end of the varlist
			int lineNumber = myVarPos+1;
			while(lineNumber < varNumber)
				try{
					swapFile.readLine();
					lineNumber ++;
				}catch(IOException ex){
					throw new Error(ex.getMessage());
				}
		}

		xConsole.debug("myVarPos = " + myVarPos);
		xConsole.debug("varNumber = " + varNumber);

		do{//fetch the values before the current time unit
			try{
				s = swapFile.readLine();
			}catch(IOException ex){
				throw new Error(ex.getMessage());
			}

			if(pos % varNumber == myVarPos && s != null){
				xConsole.debug("added string: " + s + " at pos: " +
						pos + " last char: " + s.charAt(s.length() -1)
						+ " first char: " + s.charAt(0));
				h.addString(s);
			}
			pos++;
		}while(s != null);

		//put the varHistory into the varHistories at the correct location:
		varHistories.setElementAt(h, myVarPos);

		xConsole.debug("VarHistory is: " + h);
		return (RuntimeVarHistory) h;
	}

	/**
	 * Must be called at every update by the externalListener or whoever wants to use
	 * verHistories.
	 */
	public long getSimulationLength(){
		return Time.oClock();
	}

	//Executable implementation

	/**
	 * At the end of each timUnit flushes all the vars values into a temporary file
	 * After that, it calls each VarHistory created to update itself
	 * And after and only after that, calls the ExternalListaner to update itself
	 */
	public void execute(){
		xConsole.debug("RuntimeVCDReader executing");
		try{
			swapFile.seek(swapFile.length());
		}catch(IOException ex){};
		for(Enumeration data = dataHolders.elements(), hist = varHistories.elements() ; data.hasMoreElements() ;){
			DataHolder d = (DataHolder) data.nextElement();
			RuntimeVarHistory h = (RuntimeVarHistory) hist.nextElement();
			String s = d.toString(2);
			if(h != null){
				h.addString(s);
			}
			try{
				swapFile.writeBytes(s + "\n");
				System.out.println("writed string: " + s + " size: " + s.length());
			}catch(IOException ex){}
		}
		externalListener.timeNotify();
		Time.addAfterFinish(this);
	}

	//VeriableHistoryRegistry implementation

	/**
	 * Called by every inspectable variable inside the model, in order to keep track of its evolution in time
	 */
	public void registerVariable(DataHolder var, String name){
		dataHolders.addElement(var);
		varHistories.addElement(null); //ensure it has the correct size
		varNumber++;
		try{
			swapFile.writeBytes(name + "\n");
		}catch(IOException ex){}
	}

	public String toString(){
		String res = "RuntimeVCDReader: registered variables (pending... :< ):\n";
		return res;
	}
}





